package util;

import org.apache.http.*;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;

public class HttpClientUtil {


    private static final HttpClientBuilder httpClientBuilder = HttpClients.custom();


    public final static String[] PROXY_IP_LIST = {
            "182.101.207.11,8080",
            "171.35.165.247,8085",
            "182.101.207.11,8080",
            "171.35.165.247,8085",
            "202.109.157.61,9000",
            "113.195.6.176,8085",
            "202.109.157.61,9000",
            "113.195.6.176,8085",
            "202.109.157.65,9000",
            "202.109.157.60,9000",
            "202.109.157.65,9000",
            "202.109.157.60,9000",
            "202.109.157.62,9000",
            "171.35.165.247,8085",
            "182.101.207.11,8080",
            "202.109.157.64,9000",
            "182.101.207.11,8080",
            "202.109.157.64,9000",
            "182.87.141.222,49703",
            "182.87.141.222,49703",
            "218.64.84.117,8060",
            "218.64.84.117,8060",
            "218.204.153.156,8080",
            "220.176.168.139,9129",
            "218.204.153.156,8080",
            "220.176.168.139,9129",
            "182.101.207.11,8080",
            "202.109.157.60,9000",
            "113.195.6.176,8085",
            "113.195.6.176,8085",
            "220.176.168.139,9129",
            "220.176.168.139,9129"
    };
    public final static Random R = new Random();

    static {
        try {
            // 连接管理器
            SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
            sslContextBuilder.loadTrustMaterial(null, new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                    return true;
                }
            });

            SSLContext sslContext = sslContextBuilder.build();

            SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
                    sslContext,
                    new String[]{
                            "SSLv2Hello",
                            "SSLv3",
                            "TLSv1",
                            "TLSv1.1",
                            "TLSv1.2"
                    },
                    null,
                    NoopHostnameVerifier.INSTANCE
            );

            Registry<ConnectionSocketFactory> registry = RegistryBuilder
                    .<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    .register("https", sslConnectionSocketFactory)
                    .build();

            PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(registry);
            poolingHttpClientConnectionManager.setMaxTotal(50);// 连接池最大连接数量
            poolingHttpClientConnectionManager.setDefaultMaxPerRoute(50); // 每个路由的默认连接 路由 = ip + port

            httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);

            // 请求默认配置
            RequestConfig requestConfig = RequestConfig
                    .custom()
                    .setConnectTimeout(6000)
                    .setSocketTimeout(3000)
                    .setConnectionRequestTimeout(6000)
                    .build();
            httpClientBuilder.setDefaultRequestConfig(requestConfig);

            // 默认请求头配置
            List<Header> headerList = new ArrayList<>();
            BasicHeader agentHeader = new BasicHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Edg/94.0.992.38");

            headerList.add(agentHeader);
            httpClientBuilder.setDefaultHeaders(headerList);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 发送GET请求
     * @param url
     * @param headers
     * @return
     */
    public static String executeGetRequest(String url, Map<String, String> headers) {
        CloseableHttpClient closeableHttpClient = null;
        CloseableHttpResponse closeableHttpResponse = null;
        HttpEntity httpEntity = null;
        String resJson = null;
        try {
            // closeableHttpClient = httpClientBuilder.build();
            closeableHttpClient = HttpClients.createDefault();
            // 配置请求地址
            HttpGet httpGet = new HttpGet(url);

//            final int INDEX = R.nextInt(PROXY_IP_LIST.length);
//            final String[] PROXY_CONFIG = PROXY_IP_LIST[INDEX].split(",");
//            String ip = PROXY_CONFIG[0];
//            int port = Integer.parseInt(PROXY_CONFIG[1]);
//            HttpHost httpHost = new HttpHost(ip, port);
//
//            // 创建请求配置对象
//            RequestConfig requestConfig = RequestConfig
//                    .custom()
//                    .setProxy(httpHost) // 设置代理主机的信息
//                    .build();
//
//            httpGet.setConfig(requestConfig);

            // 配置请求头信息
            for (String key : headers.keySet()) httpGet.addHeader(key, headers.get(key));

            // 请求发送
            closeableHttpResponse = closeableHttpClient.execute(httpGet);

            // 状态判定
            StatusLine statusLine = closeableHttpResponse.getStatusLine();
            int statusCode = statusLine.getStatusCode();
            if ( ! isSuccessRequest(statusCode)) throw new Exception();

            httpEntity = closeableHttpResponse.getEntity();
            resJson = EntityUtils.toString(httpEntity, "GBK");
        } catch (Exception exception) {
            exception.printStackTrace();
            return null;
        } finally {
            try {
                EntityUtils.consume(httpEntity);
                closeableHttpResponse.close();
                closeableHttpClient.close();
            } catch (Exception exception) {
                exception.printStackTrace();
            }
        }
        return resJson;
    }

    /**
     * 发送POST请求
     * @param url
     * @param headers
     * @return
     */
    public static String executeGetRequest(String url, String json, Map<String, String> headers) {
        CloseableHttpClient closeableHttpClient = null;
        CloseableHttpResponse closeableHttpResponse = null;
        HttpEntity httpEntity = null;
        String resJson = null;
        try {
            closeableHttpClient = httpClientBuilder.build();

            // 配置请求地址
            HttpPost httpPost = new HttpPost(url);

            // 配置请求头信息
            for (String key : headers.keySet()) httpPost.addHeader(key, headers.get(key));
            httpPost.addHeader("Content-Type", "application/json; charset=UTF-8");

            // 请求体设置
            StringEntity jsonEntity = new StringEntity(json, Consts.UTF_8);
            jsonEntity.setContentEncoding(Consts.UTF_8.name());
            jsonEntity.setContentType("application/json; charset=UTF-8");
            httpPost.setEntity(jsonEntity);

            // 请求发送
            closeableHttpResponse = closeableHttpClient.execute(httpPost);

            // 状态判定
            StatusLine statusLine = closeableHttpResponse.getStatusLine();
            int statusCode = statusLine.getStatusCode();
            if ( ! isSuccessRequest(statusCode)) throw new Exception(url + " 请求失败， " + statusLine);

            httpEntity = closeableHttpResponse.getEntity();
            resJson = EntityUtils.toString(httpEntity);
        } catch (Exception exception) {
            exception.printStackTrace();
            return null;
        } finally {
            try {
                EntityUtils.consume(httpEntity);
                closeableHttpResponse.close();
                closeableHttpClient.close();
            } catch (Exception exception) {
                exception.printStackTrace();
            }
        }
        return resJson;
    }


    /**
     * 这里判定条件宽泛，只要是2和3都算请求成功
     * @param statusCode
     * @return
     */
    public static boolean isSuccessRequest(int statusCode) {
        boolean flag = false;
        switch (statusCode) {
            // 2XX状态
            case HttpStatus.SC_OK:
            case HttpStatus.SC_CREATED:
            case HttpStatus.SC_ACCEPTED:
            case HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION:
            case HttpStatus.SC_NO_CONTENT:
            case HttpStatus.SC_RESET_CONTENT:
            case HttpStatus.SC_PARTIAL_CONTENT:
            case HttpStatus.SC_MULTI_STATUS:
            case HttpStatus.SC_MULTIPLE_CHOICES:
                // 3XX状态
            case HttpStatus.SC_MOVED_PERMANENTLY:
            case HttpStatus.SC_MOVED_TEMPORARILY:
            case HttpStatus.SC_SEE_OTHER:
            case HttpStatus.SC_NOT_MODIFIED:
            case HttpStatus.SC_USE_PROXY:
            case HttpStatus.SC_TEMPORARY_REDIRECT:
                flag = true;
                break;
            default:
        }
        return flag;
    }
}